package com.project.poetry.service.service.impl;

import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.project.poetry.common.constant.ConstantValue;
import com.project.poetry.common.exception.ServiceException;
import com.project.poetry.common.util.FileUtil;
import com.project.poetry.service.entity.dto.poetry.PoetryListDTO;
import com.project.poetry.service.entity.dto.poetry.PoetryListFileDTO;
import com.project.poetry.service.entity.dto.poetry.PoetryListOutDTO;
import com.project.poetry.service.entity.po.author.AuthorPO;
import com.project.poetry.service.mapper.AuthorMapper;
import com.project.poetry.service.service.CodeService;
import com.project.poetry.service.service.ExportService;
import com.project.poetry.service.service.PoetryService;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 文件业务层实现类
 * @author mengxj
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class ExportServiceImpl implements ExportService {

    @Resource
    private PoetryService poetryService;

    @Resource
    private AuthorMapper authorMapper;

    @Resource
    private CodeService codeService;

    @Override
    public void poetryListExcel(PoetryListDTO poetryList, HttpServletResponse response) {
        List<PoetryListOutDTO> dtoList = this.poetryService.poetryListFile(poetryList);
        Workbook wb = new XSSFWorkbook();
        Sheet sheet = wb.createSheet();
        Row row = sheet.createRow(0);
        for (int i = 0; i < ConstantValue.POETRY_LIST_TITLE.length; i++) {
            row.createCell(i).setCellValue(ConstantValue.POETRY_LIST_TITLE[i]);
        }
        for(int i = 0, j = dtoList.size(); i < j;){
            PoetryListOutDTO dto = dtoList.get(i);
            row = sheet.createRow(++ i);
            row.createCell(0).setCellValue(i);
            row.createCell(1).setCellValue(dto.getDynasty());
            row.createCell(2).setCellValue(dto.getAuthor());
            row.createCell(3).setCellValue(dto.getPoetryType());
            row.createCell(4).setCellValue(dto.getTitle());
            row.createCell(5).setCellValue(dto.getText());
        }
        try {
            String fileName = new String("诗词".getBytes(StandardCharsets.UTF_8), "ISO8859-1");
            response.setHeader("Content-disposition", "attachment; filename=" + fileName + ".xlsx");
            try (OutputStream os = response.getOutputStream()){
                wb.write(os);
                os.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void poetryListPdf(PoetryListDTO poetryList, HttpServletResponse response) {
        try {
            BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            Font fontChinese = new Font(bfChinese, 12, Font.NORMAL);
            Font blodFont = new Font(bfChinese, 18, Font.BOLD, BaseColor.BLACK);
            Document document = new Document(new RectangleReadOnly(842F, 595F));
            PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream());
            document.open();
            FileUtil.addWaterDocument(writer, "诗词");
            FileUtil.addDocumentTitle(document, bfChinese, "诗词");
            PdfPTable table = new PdfPTable(new float[]{50, 50, 80, 200, 400});
            FileUtil.setTableLockedAndCenter(table);
            table.addCell(FileUtil.getBorderStyleCell(this.getQueryCriteria(poetryList), fontChinese, 1, 5, 15));
            table.addCell(FileUtil.getBorderStyleCell(" ", fontChinese, 1, 5, 15));
            FileUtil.initPdfTableTitle(table, blodFont, ConstantValue.POETRY_LIST_TITLE_PDF);
            List<PoetryListOutDTO> dtoList = this.poetryService.poetryListFile(poetryList);
            List<PoetryListFileDTO> resList = new ArrayList<>();
            this.getPoetryList(dtoList, resList);
            this.sortPoetryListFile(resList);
            resList.forEach(file -> this.addFileCell(file, table, fontChinese, 0));
            document.add(table);
            String fileName = new String("诗词".getBytes(StandardCharsets.UTF_8), "ISO8859-1");
            response.setHeader("Content-disposition", "attachment; filename="+fileName+".pdf");
            OutputStream out = response.getOutputStream();
            response.flushBuffer();
            document.close();
            out.flush();
            writer.close();
            out.close();
        }catch (Exception e){
            throw new ServiceException(e);
        }
    }

    /**
     * 筛选条件
     * @param poetry 条件对象
     * @return 筛选条件
     */
    private String getQueryCriteria(PoetryListDTO poetry){
        String o;
        boolean flag = false;
        StringBuilder builder = new StringBuilder("筛选条件  ");
        if(StrUtil.isNotBlank(o = poetry.getTitle())){
            flag = true;
            builder.append(" 题目：").append(o);
        }
        if(StrUtil.isNotBlank(o = poetry.getText())){
            flag = true;
            builder.append(" 内容：").append(o);
        }
        Long aId;
        if(Objects.nonNull(aId = poetry.getAuthor()) && !aId.equals(-1L)){
            flag = true;
            AuthorPO author = this.authorMapper.selectById(aId);
            builder.append(" 作者：").append(author.getAuthor());
        }
        if(StrUtil.isNotBlank(o = poetry.getDynasty())){
            flag = true;
            builder.append(" 朝代：").append(o);
        }
        Integer tId;
        if(Objects.nonNull(tId = poetry.getType()) && !tId.equals(-1)){
            flag = true;
            builder.append(" 类型：").append(this.codeService.getValueByCode(tId));
        }
        return flag ? builder.toString() : "全部作品";
    }

    private int deepValue = 3;

    private List<Function<PoetryListOutDTO, String>> funcList = Arrays.asList(
            PoetryListOutDTO :: getDynasty,
            PoetryListOutDTO :: getAuthor,
            PoetryListOutDTO :: getPoetryType);

    /**
     * 递归添加单元格
     * @param file 数据对象
     * @param table table
     * @param fontChinese 字体
     * @param lastIndex 上次的下标
     */
    private void addFileCell(PoetryListFileDTO file, PdfPTable table, Font fontChinese, int lastIndex){
        if(++ lastIndex == deepValue){
            List<PoetryListFileDTO.PoetryListFileInDTO> list = file.getList();
            table.addCell(FileUtil.getCell(file.getKey(), fontChinese, list.size()));
            for(PoetryListFileDTO.PoetryListFileInDTO inDto : list){
                table.addCell(FileUtil.getCell(inDto.getTitle(), fontChinese));
                table.addCell(FileUtil.getCell(inDto.getText(), fontChinese));
            }
        }else{
            table.addCell(FileUtil.getCell(file.getKey(), fontChinese, file.getNum()));
            List<PoetryListFileDTO> list = file.getValue();
            this.sortPoetryListFile(list);
            for(PoetryListFileDTO f : list){
                this.addFileCell(f, table, fontChinese, lastIndex);
            }
        }
    }

    /**
     * 处理作品数据
     * @param poetryList 数据源
     * @param resList 结果
     * @see ExportServiceImpl#initRes
     * @see ExportServiceImpl#getSortMap
     * @see ExportServiceImpl#init
     */
    public void getPoetryList(List<PoetryListOutDTO> poetryList, List<PoetryListFileDTO> resList){
        this.initRes(poetryList, resList, 0, this.getSortMap(poetryList));
        resList.forEach(this :: init);
    }

    /**
     * 对list按照PoetryListFileDTO得sort属性的值进行排序
     * @param list PoetryListFileDTO集合
     * @see PoetryListFileDTO
     */
    private void sortPoetryListFile(List<PoetryListFileDTO> list){
        list.sort(Comparator.comparingInt(PoetryListFileDTO :: getSort));
    }

    /**
     * 获取形如
     * {
     *     authorId: 1,
     *     poetryType: 诗词类型字典,
     *     dynasty：朝代字典
     * }
     * 的map结构的数据。用来排序
     * @param poetryList 查询结果集
     * @return map结构数据
     */
    private Map<String, Integer> getSortMap(List<PoetryListOutDTO> poetryList){
        Map<String, Integer> res = new HashMap<>(32);
        poetryList.forEach(dto -> {
            res.put(dto.getAuthor(), Integer.valueOf(dto.getAuthorId().toString()));
            res.put(dto.getPoetryType(), dto.getTypeCode());
            res.put(dto.getDynasty(), dto.getDynastyCode());
        });
        return res;
    }

    /**
     * 递归将结果处理成树形结构
     * @param list 数据库查询出的数据
     * @param res 树形结构的容器
     * @param lastIndex 下标，用来判断递归深度
     * @param sortMap 排序map
     */
    private void initRes(List<PoetryListOutDTO> list, List<PoetryListFileDTO> res, int lastIndex, Map<String, Integer> sortMap){
        int index = lastIndex + 1;
        Map<String, List<PoetryListOutDTO>> map = list.stream().collect(Collectors.groupingBy(funcList.get(lastIndex)));
        map.forEach((k, v) -> {
            PoetryListFileDTO inRes = new PoetryListFileDTO();
            res.add(inRes);
            inRes.setSort(sortMap.get(k));
            inRes.setKey(k);
            List<PoetryListFileDTO> resList = new ArrayList<>();
            inRes.setValue(resList);
            if(index == deepValue){
                inRes.setList(v.stream().map(poetry -> {
                    PoetryListFileDTO.PoetryListFileInDTO inDto = new PoetryListFileDTO.PoetryListFileInDTO();
                    inDto.setText(poetry.getText());
                    inDto.setTitle(poetry.getTitle());
                    return inDto;
                }).collect(Collectors.toList()));
            }else{
                this.initRes(v, resList, index, sortMap);
            }
        });
    }

    /**
     * 计算树形结构的每个根的子叶个数
     * @param res 树形结构数据
     */
    private void init(PoetryListFileDTO res){
        int sum1 = 0, sum2 = 0;
        List<PoetryListFileDTO> list1 = res.getValue();
        for(PoetryListFileDTO r1 : list1){
            List<PoetryListFileDTO> list2 = r1.getValue();
            for(PoetryListFileDTO r2 : list2){
                int num = r2.getList().size();
                sum1 += num;
                sum2 += num;
                r2.setNum(num);
            }
            r1.setNum(sum2);
            sum2 = 0;
        }
        res.setNum(sum1);
    }
}
